#include "stdlib.h"
#include "string.h"
#include <stdio.h>
#include <netinet/in.h>
#include "simclist_impl.h"
#include "../ping.h"


int string_file_comparator(const void *a, const void *b) {
	const elem_file_t *alfa=(elem_file_t *)a;
	const elem_file_t *beta=(elem_file_t *)b;
	if( strcmp(alfa->nome, beta->nome)>0 ) return -1;
	if( strcmp(alfa->nome, beta->nome)<0 ) return  1;
	return 0;
}

int ping_comparator(const void *a, const void *b) {
	const elem_ip_ping_t *alfa=(elem_ip_ping_t *)a;
	const elem_ip_ping_t *beta=(elem_ip_ping_t *)b;
	if( alfa->ping_time > beta->ping_time) return -1;
	if( alfa->ping_time < beta->ping_time ) return  1;
	return 0;
}

int ip_comparator(const void *a, const void *b){
	const elem_superpeer_t *alfa=(elem_superpeer_t *)a;
	const elem_superpeer_t *beta=(elem_superpeer_t *)b;
	if( alfa->ip > beta->ip) return -1;
	if( alfa->ip < beta->ip ) return  1;
	return 0;
}

int peer_comparator(const void *a, const void *b) {
	const elem_superpeer_t *alfa=(elem_superpeer_t *)a;
	const elem_superpeer_t *beta=(elem_superpeer_t *)b;
	if( alfa->peer_number > beta->peer_number) return -1;
	if( alfa->peer_number < beta->peer_number ) return  1;
	return 0;
}

int string_file_seeker(const void *a, const void *b) {
	if(a==NULL || b==NULL) return -1;
	const elem_file_t *alfa=(elem_file_t *)a;
	const char *beta=(char *)b;
	if( strcmp(alfa->nome, beta)==0 ) return 1;
	else return 0;
}

int string_seeker(const void *a, const void *b) {
	if(a==NULL || b==NULL) return -1;
	const char *alfa=(char *)a;
	const char *beta=(char *)b;
	if( strcmp(alfa, beta)==0 ) return 1;
	else return 0;
}

int superpeer_ip_seeker(const void *a, const void *b) {
	if(a==NULL || b==NULL) return -1;
	const elem_superpeer_t *alfa=(elem_superpeer_t *)a;
	const in_addr_t *beta=(in_addr_t *)b;
	if( alfa->ip==*beta ) return 1;
	return 0;
}

int ip_file_seeker(const void *a, const void *b) {
	if(a==NULL || b==NULL) return -1;
	const in_addr_t *alfa=(in_addr_t *)a;
	const in_addr_t *beta=(in_addr_t *)b;
	if( *alfa==*beta ) return 1;
	return 0;
}


void *element_serializer_superpeer_list(const void *restrict el, uint32_t *restrict serializ_len) {
//	const elem_superpeer_t *alfa=(elem_superpeer_t *)el;
//	*serializ_len=sizeof(*alfa);
//	void *buffer=malloc(*serializ_len);
//	memcpy(buffer,alfa,*serializ_len);
//	return buffer;
	const elem_superpeer_t *alfa=(elem_superpeer_t *)el;
	*serializ_len=12;
	void *buffer=malloc(12);
	memcpy(buffer,alfa,12);
	return buffer;
}

void *element_unserializer_superpeer_list(const void *restrict data, uint32_t *restrict data_len) {
//	const elem_superpeer_t *alfa=malloc(sizeof(elem_superpeer_t));
//	memcpy(alfa,data,*data_len);
//	return alfa;
	const elem_superpeer_t *alfa=malloc(12);
	memcpy(alfa,data,12);
	return alfa;
}

void *element_serializer_string(const void *restrict el, uint32_t *restrict serializ_len) {
	const char *alfa=(char *)el;
	*serializ_len=(50);
	void *buffer=malloc(50);
	memcpy(buffer,alfa,*serializ_len);
	return buffer;
}

void *element_unserializer_string(const void *restrict data, uint32_t *restrict data_len) {
	const char *alfa=malloc(50);
	memcpy(alfa,data,*data_len);
	return alfa;
}

size_t list_superpeer_meter(const void *el) {
	return 30;
	//return sizeof(el);
}

size_t list_file_meter(const void *el) {
	const elem_file_t *alfa=(elem_file_t *)el;
	size_t size=sizeof(alfa->lista_peer) + strlen(alfa->nome) + 100;
	return size;
}

void add_file(list_t *list,char *nome,in_addr_t ip) {
	elem_file_t *elem_seek = (elem_file_t*)list_seek(list,nome);         // trovo l'elemento in base alla funzione string_seeker
	if( elem_seek==NULL  ) {
        elem_file_t el;
		el.nome=nome;

		list_t *lista_peer=malloc(sizeof(list_t));
		if(list_init(lista_peer)==-1) {
			perror("ERRORE");
			fflush(stdout);
		}
	    list_attributes_comparator(lista_peer,list_comparator_string);
	    list_attributes_copy(lista_peer,list_meter_uint32_t,1);
        list_attributes_seeker(lista_peer,ip_file_seeker);

		if(list_append(lista_peer,&ip)<0) {
			perror("ERRORE IN LISTA\n");
			return;
		}

		el.lista_peer=lista_peer;
		list_sort(list,1);                            // ogni inserimento mantiene la lista ordinata

		if(list_append(list,&el) <0) {
			perror("ERRORE IN LISTA\n");
			return;
		}
		return;
	}
	else {
		list_t *peer_list= elem_seek->lista_peer;
		in_addr_t *ip_seek = (in_addr_t*)list_seek(peer_list,&ip);
		if(ip_seek==NULL) {                            // ip già registrato nell'avere il file una
			if(list_append(peer_list,&ip)<0) {
				perror("ERRORE IN LISTA");
			}
			list_sort(peer_list,1);                            // ogni inserimento mantiene la sottolista dei peer ordinata
		}
	}
	return;
}

void delete_file(list_t *list,char *nome,in_addr_t ip) {
	elem_file_t *elem_seek = (elem_file_t*)list_seek(list,nome);         // trovo l'elemento in base alla funzione string_seeker
	if( elem_seek!=NULL  ) {
		list_t *peer_list= elem_seek->lista_peer;
		in_addr_t *ip_seek = (in_addr_t*)list_seek(peer_list,&ip);
		if(ip_seek!=NULL) {
			list_delete(peer_list,ip_seek);
			list_sort(peer_list,1);
			if(list_size(peer_list)==0) list_delete(list,elem_seek);
		}
	}
	return;
}


void add_superpeer(list_t* list,in_addr_t ip) {
	elem_superpeer_t *elem_seek = (elem_superpeer_t*)list_seek(list,&ip);
	if(elem_seek==NULL) {                                 // la lista non contiene duplicati
		elem_superpeer_t elem;
		elem.ip=ip;
		elem.peer_number=0;                                        // nessun peer associato al momento della creazione
		list_append(list,&elem);
	    list_sort(list,1);
	}
	return;
}

void delete_superpeer(list_t* list,in_addr_t ip) {
	elem_superpeer_t *elem_seek = (elem_superpeer_t*)list_seek(list,&ip);
	if(elem_seek!=NULL) {
		list_delete(list,elem_seek);
		list_sort(list,1);
	}
	return;
}

void update_peer_number(list_t* list,in_addr_t ip,int peer_number ) {
	elem_superpeer_t *elem_seek = list_seek(list,&ip);
    if(elem_seek!=NULL) {
		elem_seek->peer_number=peer_number;               // aggiorno numero di peer
		list_sort(list,1);                               // ripristino l'ordine nella lista
    }
    return;
}


list_t *get_peer_list(list_t *list,char *nome) {      // questa funzione restituisce la lista degli ip che hanno il file richiesto
	elem_file_t *elem_seek = list_seek(list,nome);
	if(elem_seek==NULL) return NULL;
	return elem_seek->lista_peer;
}

void print_file_list(list_t *list) {
	list_iterator_start(list);
	        while (list_iterator_hasnext(list)) {
	    	elem_file_t *e=((elem_file_t *)(list_iterator_next(list)) );
	        printf("%s",e->nome);
	        printf(" ---> ");
	        fflush(stdout);
	        list_t *lista_peer =  e->lista_peer;
	        list_iterator_start(lista_peer);
	        while (list_iterator_hasnext(lista_peer)) {
	        	in_addr_t *ip=((in_addr_t *)(list_iterator_next(lista_peer)) );
	        	struct in_addr e;
	        	e.s_addr=*ip;
	        	printf("%s   ",inet_ntoa(e));
	        	fflush(stdout);
	        }
	        list_iterator_stop(lista_peer);
	        printf("\n");
	    }
	    list_iterator_stop(list);
		return;
}


void print_superpeer_list(list_t *list) {
	struct in_addr in;
	list_attributes_comparator(list,peer_comparator);
	list_sort(list,1);
	list_attributes_comparator(list,ip_comparator);
	list_iterator_start(list);
	        while (list_iterator_hasnext(list)) {
	    	elem_superpeer_t *e=((elem_superpeer_t *)(list_iterator_next(list)) );
	    	in.s_addr=e->ip;
	        printf("%s -->  ", inet_ntoa(in));
	        printf("%d\n",e->peer_number);
	    }
	    list_iterator_stop(list);
		return;
}

list_t *list_sort_by_ping(list_t *list) {
	list_t *ping_list=malloc(sizeof(list_t));
	list_init(ping_list);
	list_attributes_comparator(ping_list,ping_comparator);   // ping comparator per ordinare la lista

	list_iterator_start(list);
	        while (list_iterator_hasnext(list)) {
	    	elem_superpeer_t *e=((elem_superpeer_t *)(list_iterator_next(list)) );
	        elem_ip_ping_t node;
	        node.ip=e->ip;
	    	node.ping_time=ping_ip_time(e->ip);
	    	list_append(ping_list,&node);
	    	list_sort(ping_list,1);                   // ordine crescente per ping time
	    }
	    list_iterator_stop(list);
	    return ping_list;
}

void list_superpeer_copy(list_t *src,list_t *dest ) {
	list_iterator_start(src);
	while(list_iterator_hasnext(src)) {
		elem_superpeer_t *current=list_iterator_next(src);
		add_superpeer(dest,current->ip);
	}
	list_iterator_stop(src);
}



